# package pgp

# import (
# 	"bufio"
# 	"bytes"
# 	"errors"
# 	"fmt"
# 	"io"
# 	"io/ioutil"
# 	"os"
# 	"os/exec"
# 	"path/filepath"
# 	"strings"
# )

# // Test interface
# var (
# 	_ Signer   = &GpgSigner{}
# 	_ Verifier = &GpgVerifier{}
# )
from pgp.gnupg_finder import *
from pgp.pgp import *
import os
import tempfile
import subprocess
# // GpgSigner is implementation of Signer interface using gpg as external program
class GpgSigner :
	gpg                        :str
	version                    :GPGVersion
	keyRef                     :str
	keyring:str
	secretKeyring     :str
	passphrase:str
	passphraseFile :str
	batch                      :bool=None


	# // SetBatch control --no-tty flag to gpg
	def SetBatch(g,batch :bool) :
		g.batch = batch
	

	# // SetKey sets key ID to use when signing files
	def SetKey(g,keyRef :str):
		g.keyRef = keyRef
	

	# // SetKeyRing allows to set custom keyring and secretkeyring
	def SetKeyRing(g,keyring, secretKeyring :str) :
		g.keyring, g.secretKeyring = keyring, secretKeyring
	

	# // SetPassphrase sets passphrase params
	def SetPassphrase(g,passphrase, passphraseFile :str) :
		g.passphrase, g.passphraseFile = passphrase, passphraseFile
	

	def  gpgArgs(g) ->List[str] :
		args = []
		if g.keyring != "" :
			args +=[ "--no-auto-check-trustdb", "--no-default-keyring", "--keyring", g.keyring]
		
		if g.secretKeyring != "" and g.version == GPG1x :
			args += ["--secret-keyring", g.secretKeyring]
	

		if g.keyRef != "" :
			args +=["-u", g.keyRef]

		if g.passphrase != "" or g.passphraseFile != "" :
			if g.version == GPG1x :
				args .append( "--no-use-agent")
			
		

		if g.passphrase != "" :
			args+=[ "--passphrase", "'{}'".format(g.passphrase)]
		

		if g.passphraseFile != "" :
			args +=[ "--passphrase-file", g.passphraseFile]
		

		if g.batch :
			args +=[ "--no-tty", "--batch"]
			if g.version >= GPG21x :
				args += ["--pinentry-mode", "loopback"]
			
		

		return args




	# // Init verifies availability of gpg & presence of keys
	def  Init(g) ->str :
		output= os.popen(  "{} --list-keys --dry-run --no-auto-check-trustdb".format(g.gpg)).readlines()
		err=None
		# if err is not None  :
		# 	return  ("unable to execute gpg: %s (is gpg installed?): %s", err, string(output))
		

		if g.keyring == "" and g.secretKeyring == "" and len(output) == 0 :
			return  ("looks like there are no keys in gpg, please create one (official manual: http://www.gnupg.org/gph/en/manual.html)")
		

		return err
	

	# // DetachedSign signs file with detached signature in ASCII format
	def  DetachedSign(g,source :str, destination :str) :
		print("Signing file '{}' with gpg, please enter your passphrase when prompted:".format(os.path.basename(source)))

		args = ["-o", destination, "--digest-algo", "SHA256", "--armor", "--yes"]
		args += g.gpgArgs()
		args+=["--detach-sign", source]
		print('----------','{} {}'.format(g.gpg, ' '.join(args)))
		cmd = os.popen('{} {}'.format(g.gpg, ' '.join(args)))
		cmd.read()
		return None#cmd.Run()
	

	# // ClearSign clear-signs the file
	def ClearSign(g,source :str, destination :str) :
		print("Clearsigning file '{}' with gpg, please enter your passphrase when prompted:\n".format(os.path.basename(source)))
		args = ["-o", destination, "--digest-algo", "SHA256", "--yes"]
		args += g.gpgArgs()
		args += ["--clearsign", source]
		print('----------','{} {}'.format(g.gpg, ' '.join(args)))
		cmd  = os.popen('{} {}'.format(g.gpg, ' '.join(args)))
		cmd.read()
		# cmd.Stdin = os.Stdin
		# cmd.Stdout = os.Stdout
		# cmd.Stderr = os.Stderr
		return None#cmd.Run()
	



# // NewGpgSigner creates a new gpg signer
def NewGpgSigner(finder :GPGFinder) ->GpgSigner :
	gpg, version, err = finder.FindGPG()
	print('********',gpg)
	if err is not None  :
		raise Exception(err)
	gpgSigner=GpgSigner()
	gpgSigner.gpg=gpg
	gpgSigner.version=version
	return gpgSigner



# // GpgVerifier is implementation of Verifier interface using gpgv as external program
class GpgVerifier:
	gpg      :str=None
	gpgv     :str=None
	version  :GPGVersion=None
	keyRings :List[str]=None

	# // InitKeyring verifies that gpg is installed and some keys are trusted
	def InitKeyring(g) ->str:
		if len(g.keyRings) == 0 :
			# // using default keyring
			output, err = subprocess.Popen("{} {}".format(g.gpg, ' '.join["--no-default-keyring", "--no-auto-check-trustdb", "--keyring", "trustedkeys.gpg", "--list-keys"]))
			if err is None  and len(output) == 0 :
				print("\nLooks like your keyring with trusted keys is empty. You might consider importing some keys.\n")
				print("If you're running Debian or Ubuntu, it's a good idea to import current archive keys by running:\n\n")
				print("  gpg --no-default-keyring --keyring /usr/share/keyrings/debian-archive-keyring.gpg --export | gpg --no-default-keyring --keyring trustedkeys.gpg --import\n")
				print("\n(for Ubuntu, use /usr/share/keyrings/ubuntu-archive-keyring.gpg)\n\n")
			
		

		return None
	

	# // AddKeyring adds custom keyring to GPG parameters
	def AddKeyring(g,keyring :str) :
		g.keyRings . append( keyring)
	

	def  argsKeyrings(g) ->List[str]:
		if len(g.keyRings) > 0 :
			args = []#make([]string, 0, 2*len(g.keyRings))
			for keyring in g.keyRings :
				args+=["--keyring", keyring]
			
		else :
			args = ["--keyring", "trustedkeys.gpg"]
		
		return args
	

	def  runGpgv(g,args :List[str], context :str, showKeyTip :bool) :
		
		args=args+["--status-fd", "3"]
		tempf= tempfile.NamedTemporaryFile("aptly-gpg-status")
		cmd = subprocess.Popen('{} {}'.format(g.gpgv, ' '.join(args)),stdout=tempf.name,stderr=subprocess.PIPE)
		

		# defer tempf.Close()

		# err = os.remove(tempf.Name())


		# cmd.ExtraFiles = []#*os.File{tempf}

		stderr= cmd.stderr

		err = cmd.Start()
		# if err is not None  {
		# 	return   None , err
		# }

		# buffer = cmd.stderr # &bytes.Buffer{}

		# _, err = io.Copy(io.MultiWriter(os.Stderr, buffer), stderr)
		# if err is not None  {
		# 	return   None , err
		# }

		# cmderr := cmd.Wait()

		tempf.seek(0, 0)

		# statusr := bufio.NewScanner(tempf)

		result = KeyInfo()

		with open(tempf.name) as f:
			for line in f:
				line = line.strip()
				lineSplit=line.split(' ')
				if line.startswith( "[GNUPG:] GOODSIG ") :
					result.GoodKeys .append( Key(lineSplit[2]))
				elif line.startswith( "[GNUPG:] NO_PUBKEY ") :
					result.MissingKeys.append(Key(lineSplit[2]))
				
		

		# if err = statusr.Err(); err is not None  {
		# 	return   None , err
		# }

		if stderr is not None  :
			if showKeyTip and len(g.keyRings) == 0 and len(result.MissingKeys) > 0 :
				print("\nLooks like some keys are missing in your trusted keyring, you may consider importing them from keyserver:\n\n")

				keys =[]# make([]string, len(result.MissingKeys))

				for i in  result.MissingKeys :
					keys[i] = (result.MissingKeys[i])
				
				print("gpg --no-default-keyring --keyring trustedkeys.gpg --keyserver keyserver.ubuntu.com --recv-keys %s\n\n",
					''.join(keys))

				print("Sometimes keys are stored in repository root in file named Release.key, to import such key:\n\n")
				print("wget -O - https://some.repo/repository/Release.key | gpg --no-default-keyring --keyring trustedkeys.gpg --import\n\n")
			
			return result,  "verification of {} failed: {}".format(context, stderr)
		
		return result,   None 
	

# // VerifyDetachedSignature verifies combination of signature and cleartext using gpgv
	def  VerifyDetachedSignature(g,signature, cleartext , showKeyTip :bool) :
		args = g.argsKeyrings()

		sigf = tempfile.NamedTemporaryFile( "aptly-gpg")
		
		# defer os.Remove(sigf.Name())
		# defer sigf.Close()
		# 把signature复制到sigf
		# _, err = io.Copy(sigf, signature)
		# with open(sigf.name,'w') as f:
		sigf.write(signature)
		# if err is not None  {
		# 	return err
		# }

		clearf= tempfile.NamedTemporaryFile("aptly-gpg")
		# if err is not None  {
		# 	return err
		# }
		# defer os.Remove(clearf.Name())
		# defer clearf.Close()
		#把cleartext复制到clearf
		# _, err = io.Copy(clearf, cleartext)
		# if err is not None  {
		# 	return err
		# }
		# with open(clearf.name,'w') as f:
		clearf.write(cleartext)

		args.append(sigf.name, clearf.name)
		_, err = g.runGpgv(args, "detached signature", showKeyTip)
		clearf.close()
		sigf.close()
		return err


# // IsClearSigned returns true if file contains signature
	def   IsClearSigned(g,clearsigned )-> (bool, str) :
		scanner = clearsigned.split('\n')
		for line in  scanner :
			if  "BEGIN PGP SIGN" in line:
				return True,   None 
			
		

		return False, '需要查看scanner'#scanner
	

# // VerifyClearsigned verifies clearsigned file using gpgv
	def  VerifyClearsigned(g,clearsigned  , showKeyTip :bool) ->tuple[KeyInfo, str]:
		args = g.argsKeyrings()

		clearf= tempfile.NamedTemporaryFile( "aptly-gpg")
		# if err is not None  {
		# 	return   None , err
		# }
		# defer os.Remove(clearf.Name())
		# defer clearf.Close()

		# _, err = io.Copy(clearf, clearsigned)
		# if err is not None  {
		# 	return   None , err
		# }
		clearf.write(clearsigned)
		args .append( clearf.name)
		clearf.close()
		return g.runGpgv(args, "clearsigned file", showKeyTip)


# // ExtractClearsigned extracts cleartext from clearsigned file WITHOUT signature verification
	def  ExtractClearsigned(g,clearsigned ) ->tuple[str,str]:
		clearf= tempfile.NamedTemporaryFile("", "aptly-gpg")
		# if err is not None  {
		# 	return
		# }
		# defer os.Remove(clearf.Name())
		# defer clearf.Close()
		clearf.write(clearsigned)
		# _, err = io.Copy(clearf, clearsigned)
		# if err is not None  {
		# 	return
		# }

		text, err = tempfile.NamedTemporaryFile("", "aptly-gpg")
		# if err is not None  {
		# 	return
		# }
		# defer os.Remove(text.Name())

		args = ["--no-auto-check-trustdb", "--decrypt", "--batch", "--skip-verify", "--output", "-", clearf.name]

		cmd = subprocess.Popen('{} {}'.format(g.gpg, ' '.join(args)))
		stdout = cmd.stdout
		# if err is not None  {
		# 	return   None , err
		# }
		# defer stdout.Close()

		# err = cmd.Start()
		# if err is not None  {
		# 	return   None , err
		# }
		text.write(stdout)
		# _, err = io.Copy(text, stdout)
		# if err is not None  {
		# 	return   None , err
		# }

		err = cmd.stderr

		if err is not None  :
			return   None ,  "extraction of clearsigned file failed: {}".format( err)
		

		_, err = text.Seek(0, 0)
		# if err is not None  {
		# 	return   None , err
		# }

		return text.read()
	



# // NewGpgVerifier creates a new gpg verifier
def NewGpgVerifier(finder :GPGFinder) ->GpgVerifier :
	gpg, versionGPG, err = finder.FindGPG()
	if err is not None  :
		raise Exception(err)
	

	gpgv, versionGPGV, err = finder.FindGPGV()
	if err is not None  :
		raise Exception(err)
	

	if versionGPG != versionGPGV :
		raise Exception( "gpg and gpgv versions don't match")
	
	gpgVerifier=GpgVerifier()
	gpgVerifier.gpg= gpg
	gpgVerifier.gpgv= gpgv
	gpgVerifier.version= versionGPG
	return gpgVerifier

